package br.com.massai.view.utils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import br.com.massai.model.exceptions.ErrorCore;
import br.com.massai.model.exceptions.SystemException;

public class DateUtil {
	
	private DateUtil() { }
	
	private static final Logger log = LogManager.getLogger(DateUtil.class);
	
	public static final String FORMATO_DD_MM_YYYY = "dd/MM/yyyy";
	public static final String FORMATO_YYYYMM = "yyyyMM";
	public static final String FORMATO_MMYYYY = "MMyyyy";
	public static final String FORMATO_YYYYMMDD = "yyyyMMdd";
	public static final String FORMATO_DDMMYYYY = "ddMMyyyy";
	
	public static final String FORMATO_HH_MM_SS = "HH:mm:ss";
	public static final String FORMATO_YYYY_MM_DD_HH_MM_SS = "yyyy/MM/dd HH:mm:ss";
	public static final String FORMATO_DD_MM_YYYY_HH_MM_SS = "dd/MM/yyyy HH:mm:ss";

	private static final int MINUTOS_POR_HORA = 60;

	public static final int JANEIRO = Calendar.JANUARY + 1;
	public static final int FEVEREIRO = Calendar.FEBRUARY + 1;
	public static final int MARCO = Calendar.MARCH + 1;
	public static final int ABRIL = Calendar.APRIL + 1;
	public static final int MAIO = Calendar.MAY + 1;
	public static final int JUNHO = Calendar.JUNE + 1;
	public static final int JULHO = Calendar.JULY + 1;
	public static final int AGOSTO = Calendar.AUGUST + 1;
	public static final int SETEMBRO = Calendar.SEPTEMBER + 1;
	public static final int OUTUBRO = Calendar.OCTOBER + 1;
	public static final int NOVEMBRO = Calendar.NOVEMBER + 1;
	public static final int DEZEMBRO = Calendar.DECEMBER + 1;
	public static final int ANO_MINIMO_VALIDO = 1900;
	public static final int ANO_MAXIMO_VALIDO = 2050;

	private static Map<String, String> hmNoMeses = null;

	static {
		hmNoMeses = obterNomesMeses();
	}

	/**
	 * Retorna a hora da data informada.
	 * 
	 * @param data
	 *            A data da qual se deseja obter a hora.
	 * @return A Hora do dia da data informada.
	 */
	public static int obterHora(Date data) {
		Calendar calendario = Calendar.getInstance();
		calendario.setTime(data);

		return calendario.get(Calendar.HOUR_OF_DAY);
	}

	/**
	 * Retorna o dia da data informado.
	 * 
	 * @param data
	 *            A data da qual se deseja obter o dia.
	 * @return O dia do mês da data informada.
	 */
	public static int obterDia(Date data) {
		Calendar calendario = Calendar.getInstance();
		calendario.setTime(data);

		return calendario.get(Calendar.DAY_OF_MONTH);
	}

	/**
	 * Retorna o mês da data informado.
	 * 
	 * @param data
	 *            A data da qual se deseja obter o mês.
	 * @return O mês da data informada.
	 */
	public static int obterMes(Date data) {
		Calendar calendario = Calendar.getInstance();
		calendario.setTime(data);

		return calendario.get(Calendar.MONTH) + 1;
	}

	/**
	 * Retorna o ano da data informado.
	 * 
	 * @param data
	 *            A data da qual se deseja obter o ano.
	 * @return O ano da data informada.
	 */
	public static int obterAno(Date data) {
		Calendar calendario = Calendar.getInstance();
		calendario.setTime(data);

		return calendario.get(Calendar.YEAR);
	}

	/**
	 * Retorna a maior data entre as duas informadas.
	 * 
	 * @param data1
	 *            uma data
	 * @param data2
	 *            outra data
	 * @return a maior data entre as duas informadas.
	 */
	public static Date obterMaiorData(Date data1, Date data2) {
		Date maior = data1;
		if (data1.before(data2))
			maior = data2;
		return maior;
	}

	/**
	 * Retorna um objeto Date de acordo com os dias somados.
	 * 
	 * @param data
	 *            a data a ser adicionada
	 * @param dias
	 *            o número de dias que serao adicionados
	 * @return a data somada
	 */
	public static Date adicionaDias(Date data, int dias) {
		return incrementarDia(data, dias);
	}

	/**
	 * Retorna um objeto Date de acordo com os meses somados.
	 * 
	 * @param data
	 *            a data a ser adicionada
	 * @param meses
	 *            o número de meses que serao adicionados
	 * @return a data somada
	 */
	public static Date adicionaMeses(Date data, int meses) {

		return incrementarMes(data, meses);
	}

	/**
	 * Retorna um objeto Date de acordo com os anos somados.
	 * 
	 * @param data
	 *            a data a ser adicionada
	 * @param anos
	 *            o número de anos que serao adicionados
	 * @return a data somada
	 */
	public static Date adicionaAnos(Date data, int anos) {

		Calendar calendar = Calendar.getInstance();

		calendar.setTime(data);

		calendar.add(Calendar.YEAR, anos);

		return calendar.getTime();
	}

	/**
	 * Retorna a menor data entre as duas informadas.
	 * 
	 * @param data1
	 *            uma data
	 * @param data2
	 *            outra data
	 * @return a menor data entre as duas informadas.
	 */
	public static Date obterMenorData(Date data1, Date data2) {

		Date menor = data1;

		if (data1.after(data2)) {
			menor = data2;
		}

		return menor;
	}

	/**
	 * Converte o número de minutos informado em milissegundos.
	 * 
	 * @param minutos
	 *            O número de minutos
	 * @return o número de milissegundos O resultado da conversão de minutos em
	 *         milissegundos
	 */
	public static long converterMinutosParaMilissegundos(int minutos) {

		Date data = obterDataAtual();
		Date dataFinal = calcularData(data, Calendar.MINUTE, minutos);

		return dataFinal.getTime() - data.getTime();

	}

	/**
	 * Converte o número de horas informado em milissegundos.
	 * 
	 * @param horas
	 *            O número de horas
	 * @return o número de milissegundos O resultado da conversão de horas em
	 *         milissegundos
	 */
	public static long converterHorasParaMilissegundos(int horas) {
		return converterMinutosParaMilissegundos(horas * MINUTOS_POR_HORA);
	}

	/**
	 * Converte o número de milissegundos informado em dias.
	 * 
	 * @param milissegundos
	 *            O número de milissegundos
	 * @return número de dias O resultado da conversão de milissegundos em dias
	 */
	public static int converterMilissegundosParaDias(long milissegundos) {

		int segundos = converterMilissegundosParaSegundos(milissegundos);
		int minutos = converterSegundosParaMinutos(segundos);
		int horas = converterMinutosParaHoras(minutos);
		int dias = converterHorasParaDias(horas);

		return dias;
	}

	/**
	 * Converte o número de milissegundos informado em segundos.
	 * 
	 * @param milissegundos
	 *            O número de milissegundos
	 * @return número de segundos O resultado da conversão de milissegundos em
	 *         segundos
	 */
	public static int converterMilissegundosParaSegundos(long milissegundos) {

		return (int) (milissegundos / 1000);
	}

	/**
	 * Converte o número de segundos informado em minutos.
	 * 
	 * @param segundos
	 *            O número de segundos
	 * @return número de minutos O resultado da conversão de segundos em minutos
	 */
	public static int converterSegundosParaMinutos(int segundos) {

		return segundos / 60;
	}

	/**
	 * Converte o número de minutos informado em horas.
	 * 
	 * @param minutos
	 *            O número de minutos
	 * @return número de horas O resultado da conversão de minutos em horas
	 */
	public static int converterMinutosParaHoras(int minutos) {

		return minutos / 60;
	}

	/**
	 * Converte o número de horas informado em dias.
	 * 
	 * @param horas
	 *            O número de horas
	 * @return número de dias O resultado da conversão de horas em dias
	 */
	public static int converterHorasParaDias(int horas) {

		return horas / 24;
	}

	/**
	 * Retorna a data atual.
	 * 
	 * @return a data atual
	 */
	public static Date obterDataAtual() {
		return new Date();
	}

	/**
	 * Retorna uma String com a Hora, minutos e segundos atuais.
	 * 
	 * @return uma String com a Hora, minutos e segundos atuais
	 * @throws SystemException
	 *             caso ocorra algum erro na conversão do tipo Date para String
	 */
	public static String obterHoraAtual() throws SystemException {

		Date data = obterDataAtual();
		String hora = formatar(data, FORMATO_HH_MM_SS);

		return hora;
	}

	/**
	 * Calcula uma nova data com base na data, campo e quantidade informados.
	 * Ex: Adicionando 2 meses a data atual. DateUtil.calcularData(new Date(),
	 * Calendar.MONTH, 2);
	 * 
	 * @param data
	 *            A data que serve como base para o cálculo.
	 * @param campo
	 *            O campo que deve ser calculado.
	 * @param quantidade
	 *            A quantidade que deve ser acrescentada.
	 * @return A data calculada.
	 * @see Calendar Constantes com os campos que podem ser informados.
	 */
	public static Date calcularData(Date data, int campo, int quantidade) {

		Calendar calendario = Calendar.getInstance();
		calendario.setTime(data);
		calendario.add(campo, quantidade);

		return calendario.getTime();

	} // calcularData()

	/**
	 * Calcula a quantidade de dias entre uma data e outra. não necessariamente
	 * a data1 precisa ser anterior a data2.
	 * 
	 * @param data1
	 *            A primeira data.
	 * @param data2
	 *            A segunda data.
	 * @return A quantidade de dias entre as datas fornecidas.
	 */
	public static int obterDiasEntreDatas(Date data1, Date data2) {

		Date maior = obterMaiorData(data1, data2);
		Date menor = obterMenorData(data1, data2);

		maior = obterDataZeroHora(maior);
		menor = obterDataZeroHora(menor);

		long mili1 = maior.getTime();
		long mili2 = menor.getTime();

		long diferenca = mili1 - mili2;

		return converterMilissegundosParaDias(diferenca);
	} 
	
	/**
	 * Verifica se uma data pertence ao período indicado.
	 * 
	 * @param data
	 *            A data a ser verificada
	 * @param periodoInicial
	 *            a data inicial
	 * @param periodoFinal
	 *            a data final
	 * @return true - se a data pertencer ao periodo indicado
	 * 			false - caso contrário
	 */
	public static boolean pertenceAoPeriodo(Date dataAnalisada, Date periodoInicial, Date periodoFinal) {

		periodoInicial = obterDataZeroHora(periodoInicial);
		periodoFinal = obterDataZeroHora(periodoFinal);
				
		if ( (dataAnalisada.after(periodoInicial) || dataAnalisada.equals(periodoInicial))
				&& (dataAnalisada.before(periodoFinal) || dataAnalisada.equals(periodoFinal) )){
			return true;
		}
		
		return false;
	}

	/**
	 * Retorna o número de dias úteis de um determinado mês-ano.
	 * 
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return o número de dias úteis de um determinado mês-ano
	 */
	public static int obterDiasUteisNoMes(int mes, int ano) {
		Locale loc = new Locale("pt", "br");
		Calendar cal = Calendar.getInstance(loc);
		cal.set(ano, mes, 1);

		int ultimoDiaDoMes = cal
				.getActualMaximum(Calendar.DAY_OF_MONTH);
		int qtdDiasUteis = 0;

		for (int i = 1; i <= ultimoDiaDoMes; i++) {

			int diaDaSemana = cal.get(Calendar.DAY_OF_WEEK);

			if (!(ehFinalDeSemana(diaDaSemana)))
				qtdDiasUteis++;

			cal.add(Calendar.DAY_OF_MONTH, 1);
		}

		return qtdDiasUteis;
	}

	/**
	 * Retorna o número de dias úteis entre um intervalo de dias de um
	 * determinado mês-ano.
	 * 
	 * @param dia1
	 *            dia inicial
	 * @param dia2
	 *            dia final
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * 
	 * @return o número de dias úteis entre um intervalo de dias de um
	 *         determinado mês-ano
	 */
	public static int obterDiasUteisEntreDiasNoMes(int dia1, int dia2, int mes,
			int ano) {
		Locale loc = new Locale("pt", "br");
		Calendar cal = Calendar.getInstance(loc);
		cal.set(ano, mes, dia1);
		int qtdDiasUteis = 0;
		for (int i = 1; i <= dia2; i++) {
			int diaDaSemana = cal.get(7);
			if (!ehFinalDeSemana(diaDaSemana))
				qtdDiasUteis++;
			cal.add(5, 1);
		}

		return qtdDiasUteis;
	}
	
	/**
	 * Retorna a data atual com a hora, minutos e segundos igual a zero.
	 *  
	 * @return a data atual informada com a hora, minutos e segundos igual a zero.
	 */
	public static Date obterDataAtualZeroHora() {
		return obterDataZeroHora(new Date());
	}

	/**
	 * Retorna a data informada com a hora, minutos e segundos igual a zero.
	 * 
	 * @param data
	 *            a data que tera hora, minutos e segundos zerados.
	 * @return a data informada com a hora, minutos e segundos igual a zero.
	 */
	public static Date obterDataZeroHora(Date data) {

		Calendar calendario = Calendar.getInstance();
		calendario.setTime(data);
		calendario.set(Calendar.HOUR_OF_DAY, 0);
		calendario.set(Calendar.MINUTE, 0);
		calendario.set(Calendar.SECOND, 0);
		calendario.set(Calendar.MILLISECOND, 0);

		return calendario.getTime();
	}

	/**
	 * Calcula a quantidade de anos entre uma data e outra. não necessariamente
	 * a data1 precisa ser anterior a data2.
	 * 
	 * @param data1
	 *            A primeira data.
	 * @param data2
	 *            A segunda data.
	 * @return A quantidade de anos entre as datas fornecidas.
	 */
	public static int obterAnosEntreDatas(Date data1, Date data2) {

		Date maior = obterMaiorData(data1, data2);
		Date menor = obterMenorData(data1, data2);

		Calendar calendarioIni = Calendar.getInstance();
		calendarioIni.setTime(menor);

		Calendar calendarioFim = Calendar.getInstance();
		calendarioFim.setTime(maior);

		int diaIni = calendarioIni.get(Calendar.DAY_OF_MONTH);
		int mesIni = calendarioIni.get(Calendar.MONTH);
		int anoIni = calendarioIni.get(Calendar.YEAR);

		int diaFim = calendarioFim.get(Calendar.DAY_OF_MONTH);
		int mesFim = calendarioFim.get(Calendar.MONTH);
		int anoFim = calendarioFim.get(Calendar.YEAR);

		int diferenca = anoFim - anoIni;

		if (mesIni > mesFim) {
			diferenca = diferenca - 1;
		} else {
			if (diaIni > diaFim) {
				diferenca = diferenca - 1;
			}
		}

		return diferenca;
	} 

	
	public final static int obterMesesEntreDatas(Date begin, Date end) {
		Calendar calini = Calendar.getInstance();
		Calendar calfim = Calendar.getInstance();

		calini.setTime(begin);
		calfim.setTime(end);

		int anoliq = calfim.get(Calendar.YEAR) - calini.get(Calendar.YEAR);
		int mesliq = calfim.get(Calendar.MONTH) - calini.get(Calendar.MONTH);
		int totmes = anoliq * 12 + mesliq;

		calini.set(Calendar.YEAR, calfim.get(Calendar.YEAR));
		calini.set(Calendar.MONTH, calfim.get(Calendar.MONTH));

		return (calini.after(calfim)) ? totmes - 1 : totmes;
	}
	
	/**
	 * Formata uma data no padrão fornecido.
	 * 
	 * @param data
	 *            Um objeto data.
	 * @param padrao
	 *            O padrão no qual se deseja a data.
	 * @return uma String com a data no padrao informados.
	 * @throws SystemException
	 *             caso não seja possível formatar a data para o padrão
	 *             informado.
	 */
	public static String formatar(Date data, String padrao)
			throws SystemException {

		String dataFormatada = null;

		try {

			SimpleDateFormat formatador = new SimpleDateFormat(padrao);
			formatador.setLenient(true);
			dataFormatada = formatador.format(data);

		} catch (Exception e) {
			throw new SystemException(ErrorCore.ERROR_SYSTEM, e);
		}

		return dataFormatada;
	}

	/**
	 * Formata uma data no padrão <b>dd/MM/yyyy</b>.
	 * 
	 * @param data
	 *            Um objeto data.
	 * @return uma String com a data no padrao informados.
	 * @throws SystemException
	 *             caso não seja possível formatar a data para o padrão
	 *             informado.
	 */
	public static String formatar(Date data) throws SystemException {
		return DateUtil.formatar(data, DateUtil.FORMATO_DD_MM_YYYY);
	}

	/**
	 * Retorna um objeto do tipo Date a partir de uma String no formato
	 * informado.
	 * 
	 * @param string
	 *            Uma String com uma data no formato passado.
	 * @param formato
	 *            Uma String com o formato da data.
	 * @return Um date com a data informada
	 * @throws SystemException
	 *             caso ocorra algum problema ao criar a data.
	 */
	public static Date converter(String string, String formato)
			throws SystemException {

		Date data = null;

		try {
			DateFormat format = new SimpleDateFormat(formato);
			format.setLenient(false);

			data = format.parse(string);
		} catch (Exception e) {
			log.warn(new StringBuilder().append("não foi possível converter ").append(string).append(" em um objeto java.util.Date").toString(), e);
			throw new SystemException(ErrorCore.ERROR_SYSTEM, e);
		}

		return data;
	}

	/**
	 * Retorna um objeto do tipo Date a partir da data passada no formato
	 * <b>dd/MM/yyyy</b>.
	 * 
	 * @param string
	 *            uma String contendo uma data no formato <b>dd/MM/yyyy</b>
	 * @return Um date com a data informada
	 * @throws SystemException
	 *             caso ocorra algum problema ao criar a data.
	 */
	public static Date converter(String string) throws SystemException {
		return DateUtil.converter(string, DateUtil.FORMATO_DD_MM_YYYY);
	}

	/**
	 * Retorna um Mapa com os nomes dos meses como valores e as constantes
	 * definidas na classes como chave. Ex: JANEIRO, "Janeiro".
	 * 
	 * @return um Mapa com os nomes dos meses como valores e as constantes
	 *         definidas na classes como chave.
	 */
	private static Map<String, String> obterNomesMeses() {

		Map<String, String> hmNomesMeses = new TreeMap<String, String>();
		hmNomesMeses.put(String.valueOf(JANEIRO), "Janeiro");
		hmNomesMeses.put(String.valueOf(FEVEREIRO), "Fevereiro");
		hmNomesMeses.put(String.valueOf(MARCO), "Março");
		hmNomesMeses.put(String.valueOf(ABRIL), "Abril");
		hmNomesMeses.put(String.valueOf(MAIO), "Maio");
		hmNomesMeses.put(String.valueOf(JUNHO), "Junho");
		hmNomesMeses.put(String.valueOf(JULHO), "Julho");
		hmNomesMeses.put(String.valueOf(AGOSTO), "Agosto");
		hmNomesMeses.put(String.valueOf(SETEMBRO), "Setembro");
		hmNomesMeses.put(String.valueOf(OUTUBRO), "Outubro");
		hmNomesMeses.put(String.valueOf(NOVEMBRO), "Novembro");
		hmNomesMeses.put(String.valueOf(DEZEMBRO), "Dezembro");

		return hmNomesMeses;

	} // popularNomesMeses

	/**
	 * Retorna o nome do mês informado. A chave informada deve ser um inteiro
	 * com um valor entre 1 (JANEIRO) e 12 (DEZEMBRO)
	 * 
	 * @param mes
	 *            uma String contendo um número de 1 a 12
	 * @return O nome do mês informado
	 */
	public static String obterNomeMes(String mes) {

		// Faco isso para retirar os 0 a esquerda.
		int mesInteiro = Integer.parseInt(mes);
		return hmNoMeses.get(String.valueOf(mesInteiro));

	} // getNomeMes()

	/**
	 * Retorna o nome do mês informado. A chave informada deve ser um inteiro
	 * com um valor entre 1 (JANEIRO) e 12 (DEZEMBRO).
	 * 
	 * @param mes
	 *            um inteiro contendo um número de 1 a 12
	 * @return O nome do mês informado
	 */
	public static String obterNomeMes(int mes) {
		return obterNomeMes(String.valueOf(mes));
	} // getNomeMes()

	/**
	 * Retorna o nome reduzido do mês informado. A chave informada deve ser um
	 * inteiro com um valor entre 1 (JANEIRO) e 12 (DEZEMBRO).
	 * 
	 * @param mes
	 *            uma String contendo um número de 1 a 12
	 * @return O nome do mês informado
	 */
	public static String obterNomeMesReduzido(String mes) {

		String nomeMes = obterNomeMes(mes);
		String nomeReduzido = "";

		if (nomeMes != null && nomeMes.length() > 3) {
			nomeReduzido = nomeMes.substring(0, 3);
		}

		return nomeReduzido;
	}

	/**
	 * Retorna o nome reduzido do mês informado. A chave informada deve ser um
	 * inteiro com um valor entre 1 (JANEIRO) e 12 (DEZEMBRO).
	 * 
	 * @param mes
	 *            um inteiro contendo um número de 1 a 12
	 * @return O nome do mês informado.
	 */
	public static String obterNomeMesReduzido(int mes) {
		return obterNomeMesReduzido(String.valueOf(mes));
	}

	/**
	 * Retorna um Calendar com o mês e ano especificados.
	 * 
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return um Calendar com o mês e ano especificados
	 */
	private static Calendar obterMesAno(int mes, int ano) {
		// mes - 1 porque o Calendar manipula de 0 a 11.
		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.MONTH, mes - 1);
		calendar.set(Calendar.YEAR, ano);
		return calendar;
	}

	/**
	 * Retorna uma String contendo o mês e o ano concatenados. Ex: dados
	 * <code>mês: 5</code> e <code>ano:2008</code> o resultado sera
	 * <code>052008</code>.
	 * 
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return uma String contendo o mês e o ano concatenados
	 */
	public static String obterProximoMesAno(int mes, int ano) {
		Calendar calendar = obterMesAno(mes, ano);
		calendar.add(Calendar.MONTH, 1);
		Date data = calendar.getTime();

		int mesNovo = obterMes(data);
		int anoNovo = obterAno(data);

		return new StringBuilder().append(StringUtil.completarZerosEsquerda(mesNovo, 2)).append(String.valueOf(anoNovo)).toString();
	}

	/**
	 * Obter o mesAno anterior concatenado a partir de um mes a um ano passado
	 * como argumento.
	 * 
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return uma String contendo o mês/ano anterior concatenados
	 */
	public static String obterMesAnoAnterior(int mes, int ano) {

		Calendar calendar = obterMesAno(mes, ano);
		calendar.add(Calendar.MONTH, -1);

		Date data = calendar.getTime();

		int mesNovo = obterMes(data);
		int anoNovo = obterAno(data);

		return new StringBuilder().append(StringUtil.completarZerosEsquerda(mesNovo, 2)).append(String.valueOf(anoNovo)).toString();
	}

	/**
	 * Obter a data cujo mês seja anterior ao da data atual passada como
	 * argumento.
	 * 
	 * @param dataAtual
	 *            um Date representando a data atual
	 * @return Date desejado.
	 */
	public static Date obterDataComMesAnterior(Date dataAtual) {
		return incrementarMes(dataAtual, -1);
	}

	/**
	 * Obter a data cujo mês seja posterior ao da data atual passada como
	 * argumento.
	 * 
	 * @param dataAtual
	 *            representando a data atual.
	 * @return Date desejado.
	 */
	public static Date obterDataComMesPosterior(Date dataAtual) {
		return incrementarMes(dataAtual, 1);
	}

	/**
	 * Obter a data cujo dia seja posterior ao da data atual passada como
	 * argumento.
	 * 
	 * @param dataAtual
	 *            representando a data atual.
	 * @return Date desejado.
	 */
	public static Date obterDataComDiaPosterior(Date dataAtual) {
		return incrementarDia(dataAtual, 1);
	}

	/**
	 * Obter a data cujo dia seja anterior ao da data atual passada como
	 * argumento.
	 * 
	 * @param dataAtual
	 *            representando a data atual.
	 * @return Date desejado.
	 */
	public static Date obterDataComDiaAnterior(Date dataAtual) {
		return incrementarDia(dataAtual, -1);
	}

	/**
	 * Incrementar o mês da data passada de acordo com o argumento.
	 * 
	 * @param dataAtual
	 *            representando a data atual.
	 * @param incremento
	 *            representando o incremento desejado.
	 * @return Date solicitado.
	 */
	private static Date incrementarMes(Date dataAtual, int incremento) {

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(dataAtual);
		calendar.add(Calendar.MONTH, incremento);

		return calendar.getTime();
	}

	/**
	 * Incrementar o dia da data passada de acordo com o argumento.
	 * 
	 * @param dataAtual
	 *            representando a data atual.
	 * @param incremento
	 *            representando o incremento desejado.
	 * @return Date solicitado.
	 */
	private static Date incrementarDia(Date dataAtual, int incremento) {

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(dataAtual);
		calendar.add(Calendar.DAY_OF_YEAR, incremento);

		return calendar.getTime();
	}
	
	

	/**
	 * Retorna a data atual por extenso.
	 * 
	 * @see obterDataExtenso()
	 * @return Uma String contendo a data atual por extenso.
	 */
	public static String obterDataAtualExtenso() {

		Date data = obterDataAtual();
		return obterDataExtenso(data);
	}

	/**
	 * Retorna a data por extenso. Ex: 1 de Janeiro de 2006
	 * 
	 * @param data
	 *            a data
	 * @return Uma String contendo a data por extenso.
	 */
	public static String obterDataExtenso(Date data) {

		int mes = obterMes(data);
		int ano = obterAno(data);
		int dia = obterDia(data);

		StringBuffer sb = new StringBuffer();
		sb.append(dia);
		sb.append(" de ");
		sb.append(obterNomeMes(mes));
		sb.append(" de ");
		sb.append(ano);

		return sb.toString();
	}

	/**
	 * Retorna um Date de acordo com os parâmetros dia, mes e ano passados.
	 * 
	 * @param dia
	 *            o dia
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return um Date com o dia, mês e ano especificados
	 */
	public static Date obterData(int dia, int mes, int ano) {

		Calendar calendar = Calendar.getInstance();

		calendar.set(Calendar.DAY_OF_MONTH, dia);
		calendar.set(Calendar.MONTH, mes - 1);
		calendar.set(Calendar.YEAR, ano);

		return calendar.getTime();
	}

	/**
	 * Retorna um Date de acordo com as horas somadas como parametro.
	 * 
	 * @param data
	 *            a data
	 * @param horas
	 *            a quantidade de horas que serao somados eh data
	 * @return a data acrescida da quantidade de horas especificadas
	 */
	public static Date adicionaHoras(Date data, int horas) {

		Calendar calendar = Calendar.getInstance();

		calendar.setTime(data);

		calendar.add(Calendar.HOUR, horas);

		return calendar.getTime();
	}

	/**
	 * Retorna um objeto Date de acordo com os segundos somadas como parametro.
	 * 
	 * @param data
	 *            a data
	 * @param segundos
	 *            a quantidade de segundos que serao somados eh data
	 * @return a data acrescida da quantidade de segundos especificadas
	 */
	public static Date adicionaSegundos(Date data, int segundos) {

		Calendar calendar = Calendar.getInstance();

		calendar.setTime(data);

		calendar.add(Calendar.SECOND, segundos);

		return calendar.getTime();
	}

	/**
	 * Retorna um objeto Date de acordo com as minutos somadas como parametro.
	 * 
	 * @param data
	 *            a data
	 * @param minutos
	 *            a quantidade de minutos que serao somados eh data
	 * @return a data acrescida da quantidade de minutos especificadas
	 */
	public static Date adicionaMinutos(Date data, int minutos) {

		Calendar calendar = Calendar.getInstance();

		calendar.setTime(data);

		calendar.add(Calendar.MINUTE, minutos);

		return calendar.getTime();
	}

	/**
	 * Retorna um Date de acordo com os parâmetros mes e ano passados.
	 * 
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return um Date com o mês e o ano especificados
	 */
	public static Date obterData(int mes, int ano) {

		Calendar calendar = Calendar.getInstance();

		calendar.set(Calendar.MONTH, mes - 1);
		calendar.set(Calendar.YEAR, ano);

		return calendar.getTime();
	}

	/**
	 * Retorna um Map contendo o nome de todos os meses do ano que possui como
	 * chave do Map o número do mês.
	 * 
	 * @return um Map contendo o nome de todos os meses do ano que possui como
	 *         chave do Map o número do mês
	 */
	public static Map<String, String> obterMeses() {
		return hmNoMeses;
	}
	
	
	

	

	/**
	 * Retorna o último dia do mês e ano informado. Ex: 2,2004 --> 29 2,2005 -->
	 * 28 3,2005 --> 31
	 * 
	 * @param mes
	 *            o mês
	 * @param ano
	 *            o ano
	 * @return o último dia do mês e ano informado.
	 */
	public static int obterUltimoDiaMesAno(int mes, int ano) {

		Date data = obterData(mes, ano);

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(data);

		return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
	}

	/**
	 * Retorna se ano eh válido (se possui 4 digitos).
	 * 
	 * @param ano
	 *            uma String contendo o ano.
	 * @return <code>true</code> se for um ano válido, <code>false</code>
	 *         caso contrário.
	 */
	public static boolean ehAnoValido(String ano) {

		boolean ehAnoValido = true;
		
		int anoValido = Integer.parseInt(ano);
		
		if (ano.length() != 4) {
			ehAnoValido = false;
		}
		
		if(!(anoValido >= ANO_MINIMO_VALIDO && anoValido <= ANO_MAXIMO_VALIDO)){
		       ehAnoValido = false;
		}

	
		return ehAnoValido;
	}

	/**
	 * Retorna se mês eh válido (se possui 2 digitos e esta dentro do intervalo
	 * correto).
	 * 
	 * @param mes
	 *            uma String contendo o mês.
	 * @return <code>true</code> se for um mês válido, <code>false</code>
	 *         caso contrário.
	 */
	public static boolean ehMesValido(String mes) {

		boolean ehMesValido = true;

		if (mes.length() != 2) {
			ehMesValido = false;
		}

		Integer mesInteiro = Integer.valueOf(mes);

		if (mesInteiro < 1 || mesInteiro > 12) {
			ehMesValido = false;
		}

		return ehMesValido;
	}

	/**
	 * Retorna se a data especificada eh um sábado.
	 * 
	 * @param data
	 *            a data
	 * @return <code>true</code> se for sábado
	 */
	public static boolean ehSabado(Date data) {

		boolean ehSabado = false;

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(data);
		if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
			ehSabado = true;
		}

		return ehSabado;
	}

	/**
	 * Retorna se a data especificada eh um domingo.
	 * 
	 * @param data
	 *            a data
	 * @return <code>true</code> se for domingo
	 */
	public static boolean ehDomingo(Date data) {

		boolean ehDomingo = false;

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(data);
		if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
			ehDomingo = true;
		}

		return ehDomingo;
	}
	
	
	/**
	 * Valida se o ano passado eh um ano bissexto
	 * @param ano
	 * @return <code>true</code> se bissexto, false caso contrario
	 */
	public static boolean ehAnoBissexto(Integer ano) {
		
		Calendar calendar = Calendar.getInstance();
		if (calendar instanceof GregorianCalendar)
			return ((GregorianCalendar)calendar).isLeapYear(ano);
		
		return false;		
	}
	

	/**
	 * Verifica se o dia da semana eh sabado ou domingo.
	 * 
	 * @param diaDaSemana
	 *            um int correspondendo ao dia da semana
	 * @return true caso seja final de semana, false caso não seja.
	 */
	public static boolean ehFinalDeSemana(int diaDaSemana) {
		return diaDaSemana == Calendar.SATURDAY
				|| diaDaSemana == Calendar.SUNDAY;
	}

	/**
	 * Realiza a validação de uma data.
	 * 
	 * @param data
	 *            data que sera validada
	 * @return <code>true</code> se a data eh válida
	 */
	public static boolean validarData(String data) {

		DateFormat format = null;
		if (data == null)
			return false;

		if (Locale.getDefault() != null)
			format = DateFormat.getDateInstance(3, Locale.getDefault());
		else
			format = DateFormat.getDateInstance(3, Locale.getDefault());

		format.setLenient(false);

		try {
			format.parse(data);
		} catch (ParseException e) {
			return false;
		}

		return true;
	}

	/**
	 * Converte um tipo Date para String com o formato especificado.
	 * 
	 * @param data
	 *            a data
	 * @param formato
	 *            o formato
	 * @return uma String contendo uma data no formato especificado
	 */
	public static String toString(Date data, String formato) {
		SimpleDateFormat format = new SimpleDateFormat(formato);
		return format.format(data);
	}

	/**
	 * Converte uma data em String para uma data java.util.Date.
	 * 
	 * @param data
	 *            a data
	 * @param mascara
	 *            a máscara
	 * @return um Date contendo a data convertida
	 * @throws ParseException
	 *             caso ocorra algum erro ao converter a data
	 */
	public static Date toDate(String data, String mascara)
			throws ParseException {
		SimpleDateFormat format = new SimpleDateFormat(mascara);
		return format.parse(data);
	}
	
	
	/**
	 * 
	 * Realiza a validação de uma data especifica em um período determinado,
	 * que eh representado por duas datas.
	 * 
	 * Exemplos de período valido :  
	 * 			
	 * 	exemplo: data esta entre o período de validade.
	 *      dataValidar = 21/06/2009
	 *  	Periodo de validade =  19/06/2009 eh 31/06/2009
	 *  	 
	 *  	Exemplo: data eh igual ao período inicial de validade.
	 *  	dataValidar = 19/06/2009
	 *  	Período de validade =  19/06/2009 eh 31/06/2009
	 *  
	 *  	Exemplo: data eh igual ao período final de validade.3
	 *  	dataValidar = 30/06/2009
	 *  	Período de validade =  19/06/2009 eh 30/06/2009
	 *
	 * Exemplos de período invalido :  
	 * 
	 *  	 
	 *  	Exemplo: data eh posterior ao período inicial de validade.
	 *  	portanto esta fora do período valido.
	 *  	dataValidar = 30/08/2010
	 *  	Período de validade =  19/06/2009 eh 31/06/2009
	 *  
	 *  	Exemplo: data eh anterior ao período inicial de validade.
	 *  	portanto esta fora do período valido.  
	 *  	dataValidar = 18/06/2009
	 *  	Período de validade =  19/06/2009 eh 30/06/2009
	 * 
	 * 
	 * 
	 * @param dataValidar
	 *                data a ser analisada.
	 * @param dataInicio
	 *                Date data de inicio.
	 * @param dataFim
	 *                Date data de fim.
	 * 
	 * @return <true> se data estiver entre data de inicio e data de fim
	 *         <false> caso contrário.
	 * 
	 */
	public static Boolean ehPeriodoValidoEntreDatas(Date dataValidar,
			Date dataInicio, Date dataFim) {

		boolean ehPeriodoValido = false;

		
		if (((dataValidar != null && dataValidar.before(dataFim)
				&& dataValidar.after(dataInicio) || dataValidar
				.equals(dataInicio)) || dataValidar.equals(dataFim))) {
			ehPeriodoValido = true;
		}

		return ehPeriodoValido;

	}

	
	
	
	/**
	 *  Este método eh usado para retornar a data do último dia do mês.   
	 *   
	 * @return Data último dia mês.
	 * @throws Exception
	 */
	public static Date obterDataUltimoDiaMesAtual() {
		Date ultimoDiaDomes = null;
		try {
			ultimoDiaDomes = DateUtil.converter(DateUtil.obterUltimoDiaMesAno(DateUtil.obterMes(new Date()), DateUtil.obterAno(new Date())) +"/" + DateUtil.obterMes(new Date()) + "/" + DateUtil.obterAno(new Date()));
		} catch (SystemException e) {
			log.error("Erro na conversao de data",e);
		}	
		return ultimoDiaDomes;	
	}

	
	/**
	 *  Este método eh usado para retornar a data do primeiro dia do mês.   
	 *   
	 * @return Data primeiro dia mês.
	 * @throws Exception
	 */
	public static Date obterDataPrimeiroDiaMesAtual() {
		Date ultimoDiaDomes = null;
		try {
			ultimoDiaDomes = DateUtil.converter("01/" + DateUtil.obterMes(new Date()) + "/" + DateUtil.obterAno(new Date()));
		} catch (SystemException e) {
			log.error("Erro na conversao de data",e);
		}
		return ultimoDiaDomes;
	}

}
